#ifndef SCLANG_H
#define SCLANG_H

// System includes
#include <QString>
#include <QThread>
#include <QSemaphore>
#include <QReadWriteLock>
#include <QFuture>

// Local includes
//#include "Node.h"
#include "network/yigclient.h"
#include "QTimer"
#include "gui/yigscsynthbootmenu.h"


namespace sc {
    class Server;
}

class SndBuf;

/**
*   SCLang
*   The main class for the lightweight C++ implementation of SCLang. sets up start up and organizes client and server classes
*   and threads.
*/
class SCLang : public QObject
{
    Q_OBJECT

public:

    /**
    *   SCLang
    *   @param YigClient* yigClient osc networking client
    *   @param QString applicationDir The directory of the main application. Used to find the scsynth plugin directories
    *   @param int clientPort The port that our SCLang client will listen on
    *   @param int serverPort the port that SCSynth will listen on
    *   @param QObject* parent the parent QObject
    */
    SCLang(YigClient* yigClient, QString applicationDir ="", int clientPort = 57120, int serverPort = 57110, QObject* parent = 0);

    /**
    *   SCLang deconstructor
    */
    ~SCLang();

    /**
    *   setClientPor
    *   @param int clientPort The new port number for this SCLang client
    */
    void setClientPort(int clientPort);

    /**
    *   getClientPort Gets the port number for this SCLang client
    *   @return QString The port number
    */
    int getClientPort();

    /** boots the server with the correct command line arguments */
    void bootServer();

    /** quits the server and hangs until the servers has actually quit */
    void quitServer();

    /**
    *   getServerBootStatus
    *   @return bool The boot status of scsynth
    */
    bool getServerBootStatus();

    /**
    *   getSCSynth returns a pointer to the Server, used for making signal/slot connections
    *   @return Server* the pointer to the server
    */
    sc::Server* getServer();

    /**
    *   sendServer sends the server an osc command
    *   @param const char* command The osc command, such as "/quit"
    */
    void sendServer(const char* command);

    /**
    *   sendServer sends the server an osc command
    *   @param const char* command The osc command, such as "/g_freeAll"
    *   @param int nodeID the node ID of the node to be targeted for this command
    */
    void sendServer(const char* command, int nodeID);

    /**
    *   sendServer sends the server an osc command
    *   @param const char* command The osc command, such as "/g_freeAll"
    *   @param int nodeID the node ID of the node to be targeted for this command
    *   @param int value Value for the command, reference the SC docs for server commands
    */
    void sendServer(const char* command, int nodeID, const char* argument, int value);

    /**
    *   sendServer sends the server an osc command
    *   @param const char* command The osc command, such as "/g_freeAll"
    *   @param int nodeID the node ID of the node to be targeted for this command
    *   @param const char* argument The name of the argument ("freq" or "amp" for example) to be set
    *   @param float value Value for the command, reference the SC docs for server commands
    */
    void sendServer(const char* command, int nodeID, const char* argument, float value);


    /**
    *   set Sets a list of arguments and values for this node on the sc synth server, arguments and values MUST be the same length
    *   @param const char* command The osc command, such as "/n_set"
    *   @param int nodeID the node ID of the node to be targeted for this command
    *   @param QList<const char*> A list of arguments to be set
    *   @param QList<int> A list of values for the previous arguments
    */
    void sendServer(const char* command, int nodeID, QList<const char*> arguments, QList<int> values);

    /**
    *   set Sets a list of arguments and values for this node on the sc synth server, arguments and values MUST be the same length
    *   @param const char* command The osc command, such as "/n_set"
    *   @param int nodeID the node ID of the node to be targeted for this command
    *   @param QList<const char*> A list of arguments to be set
    *   @param QList<float> A list of values for the previous arguments
    */
    void sendServer(const char* command, int nodeID, QList<const char*> arguments, QList<float> values);

    void beginRecording();

    void endRecording();

    /** send the final audio out of the program via OSC */
    void pipeAudio();

public slots:

    /** This is called when the main application is attempting to quit, allowing us to clean up threads and such */
    void handleQuit();

    /** calling this slot will send the sc synth server a /status message */
    void sendStatusRequest();

    /** begins/stops recording */
    void record(bool recording);

    /** turn audio piping on/off */
    void toggleAudioPipe(bool audioPiping, QString ip, int port);

    void requestAudioBuffers(QHash<int,SndBuf*>* buffers);

private slots:

    /** This is called when the SCSynth process has completed booting */
    void handleSCSynthBoot();
    /** ends the thread that the Server class is running on */
    void handleSCSynthQuit();

signals:

    /** emitted when SCLang is booting */
    void booting();

    /** emitted when sc synth has completed its boot up process */
    void finishedBooting();

    /** emitted when SCLang is quitting */
    void quitting();

private:

    /**
    *   called when the server is done booting
    */
    void serverSetupOnBoot();

    /** packet size used for osc packets */
    static const int PACKET_BUFFER_SIZE = 1024;
    /** status of the scsynth boot, used for startup initialization */
    bool serverBootStatus;
    /** thread that the Server process will run within */
    //QThread serverThread;
    /** The object that represents SCSynth in our version of SCLang */
    //Server* server;
    sc::Server* server;
    /**  port number for this SCLang client */
    int mClientPort;
    /** osc client for networking with SCSynth */
    YigClient* scSynthClient;
    /** the port to send osc messages to for sc synth */
    int mServerPort;
    /** the id of the last synth made */
    int nodeIDCounter;
    /** the default group on the sc synth server */
    //sc::Group* defaultGroup;
    /** periodically calls for status updates */
    QTimer statusTimer;
    /** the current recording status of sclang */
    bool recording;
    /** the synth that actually does the recording */
    //sc::Synth* recordingSynth;
    /** prevents audio hang bugs after freeing synth */
    //Synth* silence;
    /** pop up when the server is booting */
    //YigSCSynthBootMenu bootMenu;
    /** bool indicating whether to send the final audio out of the program via OSC */
    bool mAudioPiping;
    /** port to send the audio pipe on */
    int audioPipePort;
    /** ip address to send the audio pipe to */
    QString audioPipeIP;
    /** lock the audio data for reading */
    QReadWriteLock locker;
    /** thread for sending audio output to a port */
    QTimer audioPipeThread;
    /** buffer for filling the audio pipe */
    SndBuf* audioPipeBuffer;
    /** used for threading audioPiping */
    QFuture<void> pipeFuture;
};

#endif // SCLANG_H
